package com.tmall.testgen.plugin.actions;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javassist.ClassClassPath;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.bytecode.MethodInfo;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.ui.IEditorActionDelegate;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.part.FileEditorInput;

import com.tmall.testgen.client.config.ConfigUtil;
import com.tmall.testgen.plugin.ConsoleFactory;
import com.tmall.testgen.plugin.wizards.runinspring.CreateTestWizardDialog;
import com.tmall.testgen.plugin.wizards.runinspring.RunInSpingWizad;

public class RunSpringAction implements IEditorActionDelegate {
	private IEditorPart targetEditor;
	private String targetClassName;
	private IProject pro;
	private ISelection selection;
	public static RunInSpingWizad wizard;
	private static CreateTestWizardDialog dialog;

	@Override
	public void run(IAction action) {
		if (selection instanceof ITextSelection) {
			
			ITextSelection ss = (ITextSelection) selection;
			String text = ss.getText();
			Method testMethod=null;
			try{
				testMethod = loadClass(text, ss.getEndLine() + 1,pro);
			}catch(Throwable e){
				ConsoleFactory.putError(e);
				errorMsg("加载异常，请查看控制台！");
				return ;
			} 
			if (testMethod != null) {
				if (wizard == null) {
					wizard = new RunInSpingWizad(pro.getProject().toString(),testMethod);
					dialog = new CreateTestWizardDialog(targetEditor.getEditorSite()
							.getShell(), wizard);
					dialog.create();
					int returnValue = dialog.open();
					if (returnValue > -1) {
						wizard = null;
						dialog = null;
					}
				} else {
					dialog.getShell().setMinimized(false);
					wizard.updateDialog(testMethod);
				}

				// RunInSpingWizad wizard = new RunInSpingWizad(testMethod);
				// MyWizardDialog dialog = new
				// MyWizardDialog(targetEditor.getEditorSite().getShell(),
				// wizard);
				// dialog.setPageSize(800,500 );
				// dialog.create();
				// dialog.open();
			} else {
				errorMsg("选中的文本:" + text + " 不是方法名或为非public方法！");
			}
		} else {
			errorMsg("选中的文本不是方法！");
		}
	}

	private void errorMsg(String msg) {
		MessageDialog.openError(targetEditor.getEditorSite().getShell(), "异常",
				msg);
	}

	@Override
	public void selectionChanged(IAction action, ISelection selection) {
		this.selection = selection;
	}

	@Override
	public void setActiveEditor(IAction action, IEditorPart targetEditor) {
		this.targetEditor = targetEditor;
		IEditorInput d = targetEditor.getEditorInput();
		if (d instanceof FileEditorInput) {
			FileEditorInput fileInput = (FileEditorInput) d;
			IFile file = fileInput.getFile();
			pro = fileInput.getFile().getProject();
			try {
				targetClassName =file.getLocation().toString();
				targetClassName = targetClassName.replace(pro.getProject().getLocation().toString(), "");

				List<String> srcs = ConfigUtil.getProjectSrcs(pro.getName());
				for (String src : srcs) {
					String prefix=src.replaceAll("/"+pro.getProject().getName(), "") ;
					if (targetClassName.startsWith(prefix)) {
						targetClassName = targetClassName.replaceFirst( prefix + "/", "");
						break;
					}
				}
				targetClassName = targetClassName.replace(".java", "");
				targetClassName = targetClassName.replaceAll("/", ".");
			} catch (Exception e) {
				errorMsg(e.getMessage());
			}
		}

	}

	/**
	 * 起始行与结束行匹配锁定函数
	 * 
	 * @param methods
	 * @param startLineNum
	 * @return
	 * @throws Exception
	 */
	private Method getMethod(List<Method> methods, Integer startLineNum)
			throws Exception {
		Map<Integer, Method> map = getMethodEndLineNum(methods);
		Set<Integer> keySet = map.keySet();
		List<Integer> keyList = new ArrayList<Integer>();

		for (Integer i : keySet) {
			keyList.add(i);
		}

		Collections.sort(keyList);
		Iterator it=keyList.iterator();
		Integer key=null;
		while(it.hasNext()){
			key=(Integer) it.next();
			if (key > startLineNum)
				return map.get(key);
		}
		return map.get(key);
	}

	/**
	 * 获取方法结束行数
	 * 
	 * @param methods
	 * @throws Exception
	 */
	private Map<Integer, Method> getMethodEndLineNum(List<Method> methods)
			throws Exception {
		Map<Integer, Method> pos = new HashMap<Integer, Method>();
		Class clazz = methods.get(0).getDeclaringClass();
		ClassPool pool = ClassPool.getDefault();
		pool.insertClassPath(new ClassClassPath(clazz));

		for (Method method : methods) {

			Class[] parameterClasses = method.getParameterTypes();
			CtClass[] parameterCtClasses = new CtClass[parameterClasses.length];

			for (int i = 0; i < parameterClasses.length; i++) {
				parameterCtClasses[i] = pool.get(parameterClasses[i].getName());
			}

			CtClass ctClass = pool.get(clazz.getName());
			CtMethod ctMethod = ctClass.getDeclaredMethod(method.getName(),
					parameterCtClasses);

			MethodInfo methodInfo = ctMethod.getMethodInfo();
			pos.put(methodInfo.getLineNumber(-1), method);
		}

		return pos;
	}

	private Map<String,ClassLoader> classLoaders=new HashMap<String,ClassLoader>();
	private Method loadClass(String methodName, int lineNum,IProject pro) throws Throwable {
			ClassLoader classLoader=null;
//			if(classLoaders.get(targetProjectPath)!=null)
//				classLoader=classLoaders.get(targetProjectPath);
//			else{
				classLoader=ConfigUtil.getClassLoader(pro.getName());
				classLoaders.put(pro.getProject().toString(), classLoader);
			//}
			 Class clazz =Class.forName(targetClassName, true, classLoader);
			Method[] mdArray = clazz.getMethods();
			List<Method> methods = new ArrayList<Method>();

			for (int i = 0; i < mdArray.length; i++) {
				if (mdArray[i].getName().equals(methodName)) {
					methods.add(mdArray[i]);
				}
			}
			if (methods.size() == 0)
				return null;

			if (methods.size() == 1) {
				return methods.get(0);
			}
			
			return getMethod(methods, lineNum);

	}

}
